﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using BrainTechLLC.ImageOperatorsNonAForgeDependent;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using AForge.Imaging;
using AForge.Imaging.Filters;
using BrainTechLLC;
using BrainTechLLC.ImageOperators;
using System.Threading;


namespace BrainTechLLC.FilterExtensions
{
    [Serializable]
    public partial class FilterCollection : ObservableCollection<ISingleFilter>, ICanApply, INotifyPropertyChanged, IPostProcessor
    {
        public void AddFilterListeners()
        {
            for (int n = 0; n < this.Count; n++)
            {
                this[n].BeforeFilterBitmap += new EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>(FilterCollection_BeforeFilterBitmap);
                this[n].AfterFilterBitmap += new EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>(FilterCollection_AfterFilterBitmap);
            }
        }

        void FilterCollection_AfterFilterBitmap(object sender, ObjectEventArgs<Bitmap, string, string, ISingleFilter> e)
        {
            _afterFilterBitmap.Raise(sender, e);
        }

        void FilterCollection_BeforeFilterBitmap(object sender, ObjectEventArgs<Bitmap, string, string, ISingleFilter> e)
        {
            _beforeFilterBitmap.Raise(sender, e);
        }

        public void RemoveFilterListeners()
        {
            for (int n = 0; n < this.Count; n++)
            {
                this[n].BeforeFilterBitmap -= FilterCollection_BeforeFilterBitmap;
                this[n].AfterFilterBitmap -= FilterCollection_AfterFilterBitmap;
            }
        }

        public void SetCurrentGeneration(int newGeneration, NotifyCollectionChangedEventArgs e)
        {
            if (_currentGeneration != newGeneration)
            {
                _currentGeneration = newGeneration;
                _generationChanged.Raise(this, e);

                OnPropertyChanged(new PropertyChangedEventArgs("CurrentGeneration"));
            }
        }

        public void GotoNextGeneration(NotifyCollectionChangedEventArgs e)
        {
            SetCurrentGeneration(CurrentGeneration + 1, e);
        }

        public void AddFilter(ISingleFilter filter)
        {
            if (filter != null)
            {
                filter.PropertyChanged += new PropertyChangedEventHandler(filter_PropertyChanged);
                this.Add(filter);
            }
        }

        public void InsertFilter(ISingleFilter filter, int location)
        {
            if (filter != null)
            {
                filter.PropertyChanged += new PropertyChangedEventHandler(filter_PropertyChanged);
                this.Insert(location, filter);
            }
        }

        private void filter_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Status")
            {
                GotoNextGeneration(null);
            }
        }

        public void AddFilter(string filterType)
        {
            Type t = AllFilters.FindFilter(filterType);
            if (t != null)
            {
                var filter = t.CreateFilter();
                if (filter != null)
                {
                    AddFilter(new SingleFilter(filter));
                }
            }
        }

        public List<Type> GetFilterTypes()
        {
            List<Type> results = new List<Type>();

            for (int n = 0; n < Count; n++)
            {
                results.Add(this[n].GetType());
            }

            return results;
        }

        public Bitmap Apply(string fileName, BitmapData imageData, bool wantPostProcessInfo, ref List<RGBAndCoordinate> postProcessInfo, ref int width, ref int height)
        {
            postProcessInfo = null;
            UnmanagedImage dstUnmanagedImage = Apply(fileName, new UnmanagedImage(imageData), wantPostProcessInfo, ref postProcessInfo, ref width, ref height);
            Bitmap dstImage = dstUnmanagedImage.ToManagedImage();
            dstUnmanagedImage.Dispose();

            return dstImage;
        }

        public UnmanagedImage Apply(string fileName, UnmanagedImage image, bool wantPostProcessInfo, ref List<RGBAndCoordinate> postProcessInfo, ref int width, ref int height)
        {
            width = 0;
            height = 0;
            postProcessInfo = null;

            // check for empty sequence
            if (Count == 0)
                throw new ApplicationException("No filters in the sequence.");            

            UnmanagedImage dstImg;
            dstImg = image.Clone();            

            UnmanagedImage tmpImg = null;
            List<ISingleFilter> list = this.ToList();

            for (int n = 0; n < list.Count; n++)
            {
                var filter = list[n];

                if (filter.Disabled)
                {
                    continue;
                }

                tmpImg = dstImg;

                if (_beforeFilterBitmap.eventEntries.Count > 0)
                {
                    OnBeforeFilterBitmap(tmpImg.ToManagedImage(), filter.Name, fileName, filter);
                }

                dstImg = filter.Apply(fileName, dstImg, wantPostProcessInfo, ref postProcessInfo, ref width, ref height);
                tmpImg.Dispose();

                if (_afterFilterBitmap.eventEntries.Count > 0)
                {
                    OnAfterFilterBitmap(dstImg.ToManagedImage(), filter.Name, fileName, filter);
                }
            }

            return dstImg;
        }

        public Bitmap Apply(string fileName, Bitmap originalBitmap, bool returnCopyIfNoAction)
        {
            List<RGBAndCoordinate> postProcessInfo = null;
            int width = 0;
            int height = 0;
            return Apply(fileName, originalBitmap, returnCopyIfNoAction, false, ref postProcessInfo, ref width, ref height);
        }

        public Bitmap GetValidBitmapCopy(Bitmap originalBitmap)
        {
            var pixelFormat = originalBitmap.PixelFormat;
            Bitmap temp;

            if (pixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb && pixelFormat != System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
            {
                if (pixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppPArgb || pixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb ||
                    pixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppRgb)
                {
                    temp = originalBitmap.Clone(new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                }
                else
                {
                    temp = originalBitmap.Clone(new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height), System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                }
            }
            else
            {
                temp = originalBitmap.Clone(new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height), originalBitmap.PixelFormat);
            }

            return temp;
        }

        public Bitmap Apply(string fileName, Bitmap originalBitmap, bool returnCopyIfNoAction, bool wantPostProcessInfo, ref List<RGBAndCoordinate> postProcessInfo, ref int width, ref int height)
        {
            postProcessInfo = null;
            Bitmap dstImage = null;
            width = 0;
            height = 0;

            if (originalBitmap == null)
                return null;

            if (Count == 0 || !Active)
            {
                if (returnCopyIfNoAction)
                {
                    return originalBitmap.Clone() as Bitmap;
                }
                else
                {
                    return originalBitmap;
                }
            }

            OnBeginFilterBitmap(originalBitmap, fileName);

            try
            {
                //OnBeforeFilterBitmap(originalBitmap, CollectionName, fileName, null);
                Timing.BeginTiming();
                Bitmap temp = GetValidBitmapCopy(originalBitmap);
                dstImage = ApplyFiltersInternal(fileName, temp, wantPostProcessInfo, ref postProcessInfo, ref width, ref height);
            }
            catch (Exception ex2)
            {
                Console.WriteLine(ex2.ToString());
            }
            finally
            {
                Timing.EndTiming((d) => AllStats.GetInstance().AddTime("AverageFilterTime", d));
            }

            //OnAfterFilterBitmap(dstImage, CollectionName, fileName, null);

            return dstImage;
        }

        public void OnBeginFilterBitmap(Bitmap bm, string fileName)
        {
            if (_beginFilterBitmap == null)
            {
                Interlocked.CompareExchange<FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string>>>>(ref _beginFilterBitmap, new FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string>>>(), null);
            }
            _beginFilterBitmap.Raise(this, new ObjectEventArgs<Bitmap, string>(bm, fileName));
        }

        public void OnBeforeFilterBitmap(Bitmap bm, string description, string fileName, ISingleFilter filter)
        {
            if (_beforeFilterBitmap == null)
            {
                Interlocked.CompareExchange<FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>>>(ref _beforeFilterBitmap, new FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>>(), null);
            }

            _beforeFilterBitmap.Raise(this, new ObjectEventArgs<Bitmap, string, string, ISingleFilter>(bm, description, fileName, filter));
        }

        public void OnAfterFilterBitmap(Bitmap bm, string description, string fileName, ISingleFilter filter)
        {
            if (_afterFilterBitmap == null)
            {
                Interlocked.CompareExchange<FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>>>(ref _afterFilterBitmap, new FastSmartWeakEvent<EventHandler<ObjectEventArgs<Bitmap, string, string, ISingleFilter>>>(), null);
            }

            _afterFilterBitmap.Raise(this, new ObjectEventArgs<Bitmap, string, string, ISingleFilter>(bm, description, fileName, filter));
        }

        private Bitmap ApplyFiltersInternal(string fileName, Bitmap temp, bool wantPostProcessInfo, ref List<RGBAndCoordinate> postProcessInfo, ref int width, ref int height)
        {
            postProcessInfo = null;
            Bitmap dstImage = null;
            width = 0;
            height = 0;

            if (temp == null)
                return null;

            // lock source bitmap data
            // was readonly
            BitmapData imageData = temp.LockBits(new Rectangle(0, 0, temp.Width, temp.Height), ImageLockMode.ReadWrite, temp.PixelFormat);

            try
            {
                dstImage = Apply(fileName, imageData, wantPostProcessInfo, ref postProcessInfo, ref width, ref height);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Debug.WriteLine(ex.ToString());
                Debug.Assert(false);
            }
            finally
            {
                temp.UnlockBits(imageData);
                temp.Dispose(); // herehere
            }

            return dstImage;
        }

        public void ClearAllFilters()
        {
            foreach (var filter in this)
            {
                filter.PropertyChanged -= filter_PropertyChanged;
            }
            Clear();
        }

        protected void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        public void RemoveFilter(ISingleFilter singleFilter)
        {
            singleFilter.PropertyChanged -= filter_PropertyChanged;
            Remove(singleFilter);
        }

        public bool ProcessBitmap(FrameInfo frameInfo)
        {
            Bitmap bm = frameInfo.Bitmap;
            bm = Apply(string.Empty, bm, false);
            frameInfo.Bitmap = bm;

            return true;
        }
    }
}
//    IFilter filterToApply = filter.Filter.MakeCopy(2, 0) as IFilter;

//    int tries = 0;

//TryAgain:
//    try
//    {
//        if (!filterToApply.IsValidPixelFormat(dstImg.PixelFormat))
//        {
//            List<System.Drawing.Imaging.PixelFormat> formats = filterToApply.GetValidPixelFormats();
//            System.Drawing.Imaging.PixelFormat format = formats[0];
//            format.TryConvertToUsableFormat(ref dstImg, ref tmpImg);
//        }

//        filterToApply.HandleGatherPixellateInfoPre(wantPostProcessInfo);

//        tmpImg = dstImg;
//        dstImg = filterToApply.Apply(tmpImg);
//        tmpImg.Dispose();

//        filterToApply.HandleGatherPixellateInfoPost(wantPostProcessInfo, ref postProcessInfo, ref width, ref height, dstImg);
//    }
//    catch (Exception ex)
//    {
//        Debug.WriteLine(ex.ToString());

//        if (tries > 0)
//            continue;

//        List<System.Drawing.Imaging.PixelFormat> formats = filter.Filter.GetValidPixelFormats();

//        System.Drawing.Imaging.PixelFormat format = formats[0];

//        if (format.TryConvertToUsableFormat(ref dstImg, ref tmpImg))
//        {
//            tries++;
//            goto TryAgain;
//        }
//    }
